/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.classdiagram.editpart;

import java.util.Iterator;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.uml2.uml.Parameter;
import org.eclipse.uml2.uml.UMLPackage;

import com.cea.papyrus.core.PapyrusPlugin;
import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.core.editpart.GraphNodeWithSemanticEditPart;
import com.cea.papyrus.core.editpart.IUMLObjectEditPart;
import com.cea.papyrus.core.figures.LabelFigure;
import com.cea.papyrus.core.helper.ModelerHelper;
import com.cea.papyrus.core.listener.UmlElementListener;
import com.cea.papyrus.core.preference.ClassDiagramCommonPreferenceConstants;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.diagram.classdiagram.figures.OperationFigure;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;
import com.cea.papyrus.umlutils.ICustomAppearence;
import com.cea.papyrus.umlutils.Operation;

/**
 * {@link org.eclipse.uml2.uml.Operation} element edit part.
 */
public class OperationEditPart extends GraphNodeWithSemanticEditPart {

	/**
	 * Creates a new OperationEditPart
	 * @param modelManager manager in charge of models, graphic and domain
	 * @param graphNode parent of the graph node controlled by this edit part
	 */
	public OperationEditPart(GraphNode graphNode,ModelManager modelManager) {
		super(graphNode,modelManager);
		// modify adapter to an operation listener
		_adapter = new UmlOperationListener(this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected IFigure createFigure() {
		return new OperationFigure();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public org.eclipse.uml2.uml.Operation getUmlElement(){
		return (org.eclipse.uml2.uml.Operation)((Uml1SemanticModelBridge)(getGraphNode()).getSemanticModel()).getElement();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void activate() {
		if (isActive()) {
			return;
		}
		super.activate();
		if (getUmlElement()!=null){
			Iterator<Parameter> it= getUmlElement().getOwnedParameters().iterator();
			while(it.hasNext()){
				addListenerToParameter(it.next());
			}
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void deactivate() {
		if (!isActive()) {
			return;
		}
		if (getUmlElement()!=null){
			Iterator<Parameter> it= getUmlElement().getOwnedParameters().iterator();
			while(it.hasNext()){
				removeListenerFromParameter(it.next());
			}
		}
		super.deactivate();
	}

	/**
	 * Adds the operation edit part listener to the specified parameter 
	 * @param parameter the parameter to listen
	 */
	protected void addListenerToParameter(Parameter parameter) {
		((Notifier)parameter).eAdapters().add(_adapter);
		// start listening for changes in the model
		if (parameter.getType()!=null) {
			((Notifier)(parameter.getType())).eAdapters().add(_adapter);
		}
		if (parameter.getUpperValue()!=null) {
			((Notifier)(parameter.getUpperValue())).eAdapters().add(_adapter);
		}
		if (parameter.getLowerValue()!=null) {
			((Notifier)(parameter.getLowerValue())).eAdapters().add(_adapter);
		}
	}

	/**
	 * Removes the operation edit part listener from the specified parameter 
	 * @param parameter the parameter not to listen anymore
	 */
	protected void removeListenerFromParameter(Parameter parameter) {
		parameter.eAdapters().remove(_adapter);
		// start listening for changes in the model
		if (parameter.getType()!=null) {
			parameter.getType().eAdapters().remove(_adapter);
		}
		if (parameter.getUpperValue()!=null) {
			parameter.getUpperValue().eAdapters().remove(_adapter);
		}
		if (parameter.getLowerValue()!=null) {
			parameter.getLowerValue().eAdapters().remove(_adapter);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected Label getNameLabel() {
		return ((OperationFigure)getFigure()).getNameLabel();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void refreshVisuals() {

		Dimension size = getGraphNode().getSize();
		Point p = getGraphNode().getPosition();
		Rectangle r = new Rectangle(p ,size);
		String newText = "";
		IPreferenceStore store = PapyrusPlugin.getDefault().getPreferenceStore();	

		OperationFigure f = (OperationFigure)getFigure();
		f.setStatic(getUmlElement().isStatic());

		/** stereotypes handling */

		/** Stereotype presentation */
		String stereotypePresentation = null;
		if(getGraphNode().getSemanticModel() != null) {
			if(getGraphNode().getSemanticModel().getPresentation() != null) {
				stereotypePresentation = getGraphNode().getSemanticModel().getPresentation();
			} else {
				getGraphNode().getSemanticModel().setPresentation(ModelerHelper.TEXT_STEREOTYPE_PRESENTATION);
				stereotypePresentation = ModelerHelper.TEXT_STEREOTYPE_PRESENTATION;
			}
		}

		// Retrieve current class and figure
		org.eclipse.uml2.uml.Operation currentOperation = getUmlElement();
		com.cea.papyrus.umlutils.Element currentElement = new com.cea.papyrus.umlutils.Element(getUmlElement());

		IFigure fig = getFigure();
		((OperationFigure) fig).refreshColors(getGraphNode());
		((LabelFigure) fig).refreshPresentation(getPresentation(), currentOperation, getGraphNode());
		boolean hasIcon  = currentElement.hasIcon();
		boolean hasShape = currentElement.hasShape();

		String stereotypes;
		stereotypes = horizontalStereotypesToDisplay();
		f.refreshStereotypes(stereotypes, getPresentation(), hasIcon, hasShape);

		if((stereotypes!="")&& 
				((stereotypePresentation != null) && (stereotypePresentation.equals(ModelerHelper.TEXT_ICON_STEREOTYPE_PRESENTATION) || 
						stereotypePresentation.equals(ModelerHelper.TEXT_STEREOTYPE_PRESENTATION)))){
			//stereotypes=stereotypes.substring(0, stereotypes.length()-2);
			f.setStereotypes(stereotypes);//add stereotypes on the operation figure
		}
		else {
			f.setStereotypes(null);
		}


		Operation utilOperation = new Operation(getUmlElement());

		int style = 0;

		// get properties on graph node for property: custom display
		com.cea.papyrus.diagraminterchange2.di2.Property prop = getGraphNode().getProperty(ICustomAppearence.CUSTOM_APPEARENCE_KEY);
		String value = "";
		// no special property
		if(prop == null) {
			style = loadPreferenceStyle(store);
		} else {
			// custom property: get the style for this property 
			value = prop.getValue();
			if(value != null) {
				try {
					style = Integer.parseInt(value);
				} catch (NumberFormatException e) {
					Debug.log(e);
				}
			}
		}

		newText = utilOperation.getCustomLabel(style);

		// displays abstract or not
		boolean isAbstract = utilOperation.isAbstract();

		// set new text
		f.display(newText, isAbstract);

		((GraphicalEditPart) getParent()).setLayoutConstraint(
				this,
				f,
				r);
	}

	/**
	 * Returns default display from preference store
	 * @param store the preference store
	 * @return default display from preference store
	 */
	private int loadPreferenceStyle(IPreferenceStore store) {
		int style = 0;

		if (ClassDiagramCommonPreferenceConstants.getPreferenceOperationNameAppearance(store)){
			style = style | ICustomAppearence.DISP_NAME;
		}
		if (ClassDiagramCommonPreferenceConstants.getPreferenceOperationVisibilityAppearance(store)){
			style = style | ICustomAppearence.DISP_VISIBILITY;
		}
		if (ClassDiagramCommonPreferenceConstants.getPreferenceOperationModifiersAppearance(store)){
			style = style | ICustomAppearence.DISP_MOFIFIERS;
		}
		if (ClassDiagramCommonPreferenceConstants.getPreferenceOperationRTTypeAppearance(store)){
			style = style | ICustomAppearence.DISP_RT_TYPE;
		}
		if (ClassDiagramCommonPreferenceConstants.getPreferenceOperationRTMultiplicityAppearance(store)){
			style = style | ICustomAppearence.DISP_RT_MULTIPLICITY;
		}
		if (ClassDiagramCommonPreferenceConstants.getPreferenceOperationParameterDirectionAppearance(store)){
			style = style | ICustomAppearence.DISP_PARAMETER_DIRECTION;
		}
		if (ClassDiagramCommonPreferenceConstants.getPreferenceOperationParameterNameAppearance(store)){
			style = style | ICustomAppearence.DISP_PARAMETER_NAME;
		}
		if (ClassDiagramCommonPreferenceConstants.getPreferenceOperationParameterTypeAppearance(store)){
			style = style | ICustomAppearence.DISP_PARAMETER_TYPE;
		}
		if (ClassDiagramCommonPreferenceConstants.getPreferenceOperationParameterMultiplicityAppearance(store)){
			style = style | ICustomAppearence.DISP_PARAMETER_MULTIPLICITY;
		}
		if (ClassDiagramCommonPreferenceConstants.getPreferenceOperationParameterDefaultValueAppearance(store)){
			style = style | ICustomAppearence.DISP_PARAMETER_DEFAULT;
		}
		if (ClassDiagramCommonPreferenceConstants.getPreferenceOperationParameterModifiersAppearance(store)){
			style = style | ICustomAppearence.DISP_PARAMETER_MODIFIERS;
		}
		return style;
	}

	/**
	 * Returns the presentation of the node (full display or short label).
	 * @return the string corresponding to the representation of the node
	 */
	@Override
	public String getPresentation() {
		String presentation = getGraphNode().getSemanticModel().getPresentation();
		// default value = "". it is set to null by the tool
		if(presentation == null) {
			presentation = "";
			setPresentation(presentation);
		}
		return presentation;
	}

	/**
	 * Set the new presentation for the node, then refresh display.
	 * @param presentation the new presentation
	 */
	public void setPresentation(String presentation) {
		getGraphNode().getSemanticModel().setPresentation(presentation);
		refresh();
	}

	/**
	 * Listener specific for {@link OperationEditPart}, as it needs to recalculate the label when parameters are changed.
	 */
	public class UmlOperationListener extends UmlElementListener {

		/**
		 * Creates a new UmlOperationListener
		 * @param editPart the edit part that listens for notifications
		 */
		public UmlOperationListener(IUMLObjectEditPart editPart) {
			super(editPart);
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void notifyChanged(Notification notification) {
			// add the operation edit part listener to a parameter if it is created and added to the operation
			if(UMLPackage.OPERATION__OWNED_PARAMETER == notification.getFeatureID(org.eclipse.uml2.uml.Operation.class) ) {
				// should always be a parameter, as it is the owned parameter collection which is modified. No need to verify cast.
				switch(notification.getEventType()) {
				case Notification.ADD:
					addListenerToParameter((Parameter)notification.getNewValue());
					break;
				case Notification.REMOVE:
					removeListenerFromParameter((Parameter)notification.getOldValue());
					// specific case because of UMLDestroyElementListener. Does not listen to parameters destruction anymore...
					umlEditPart.refresh();
					break;
				case Notification.REMOVE_MANY:
					Debug.log(IStatus.WARNING, "OperationEditPart$UmlOperationListener does not know how to handle remove many parameters");
					break;
				case Notification.ADD_MANY:
					Debug.log(IStatus.WARNING, "OperationEditPart$UmlOperationListener does not know how to handle add many parameters");
					break;
				default:
					break;
				} 
			}
			// does the same as super listener
			super.notifyChanged(notification);
		}

	}
}